package com.qiaoba.auth.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.qiaoba.api.auth.constant.SecurityConstant;
import com.qiaoba.auth.constant.OnlineUserConstant;
import com.qiaoba.auth.entity.OnlineUser;
import com.qiaoba.auth.entity.param.OnlineUserParam;
import com.qiaoba.auth.service.OnlineUserService;
import com.qiaoba.common.base.context.UserInfoContext;
import com.qiaoba.common.base.entity.BasePage;
import com.qiaoba.common.base.exception.ServiceException;
import com.qiaoba.nosql.redis.service.RedisService;
import com.qiaoba.web.util.IpUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * 在线用户 服务层实现
 *
 * @author ailanyin
 * @version 1.0
 * @since 2023/5/25 17:15
 */
@Service
@RequiredArgsConstructor
public class OnlineUserServiceImpl implements OnlineUserService {

    private final RedisService redisService;

    @Override
    public void deleteOne(String deviceSn, Boolean deleteOwn) {

        if (!deleteOwn && isOwn(deviceSn)) {
            throw new ServiceException("禁止踢出自己！");
        }
        // 删除在线用户相关信息
        OnlineUser onlineUser = deleteOnlineInfo(deviceSn);
        if (Objects.isNull(onlineUser) || StrUtil.isBlank(onlineUser.getUsername())) {
            return;
        }
        String username = onlineUser.getUsername();
        // 删除最后登陆设备
        String lastLoginDevice = redisService.getObject(OnlineUserConstant.LAST_LOGIN_USER_REDIS_KEY + username, String.class);
        if (deviceSn.equals(lastLoginDevice)) {
            redisService.del(OnlineUserConstant.LAST_LOGIN_USER_REDIS_KEY + username);
        }
        // 删除token
        redisService.del(SecurityConstant.LOGIN_USER_TOKENS_REDIS_KEY + deviceSn);
        // 删除user_details
        if (deviceSn.equals(lastLoginDevice)) {
            redisService.del(SecurityConstant.USER_DETAILS_REDIS_KEY + username);
        }
    }

    @Override
    public OnlineUser deleteOnlineInfo(String deviceSn) {
        OnlineUser onlineUser = redisService.getObject(OnlineUserConstant.USER_INFO_SET_KEY + deviceSn, OnlineUser.class);
        if (Objects.nonNull(onlineUser)) {
            String username = onlineUser.getUsername();
            // 删除在线用户信息
            redisService.del(OnlineUserConstant.USER_INFO_SET_KEY + deviceSn);
            redisService.delSortSet(OnlineUserConstant.DEVICE_HASH_KEY, deviceSn);
            redisService.lRemove(OnlineUserConstant.IP_LIST_KEY + onlineUser.getIp(), 1, deviceSn);
            redisService.lRemove(OnlineUserConstant.USERNAME_LIST_KEY + username, 1, deviceSn);
        }

        return onlineUser;
    }

    @Override
    public Boolean checkIsLastLogged(String username, String deviceSn) {
        String loggedDevice = redisService.get(OnlineUserConstant.LAST_LOGIN_USER_REDIS_KEY + username).toString();
        return deviceSn.equals(loggedDevice);
    }

    @Override
    public void insert(HttpServletRequest request, String deviceSn, String username, String nickname) {
        // Redis中已存在
        if (Objects.nonNull(redisService.getSortSetScore(OnlineUserConstant.DEVICE_HASH_KEY, deviceSn))) {
            // 更新当前时间戳
            redisService.addSortSet(OnlineUserConstant.DEVICE_HASH_KEY, deviceSn, System.currentTimeMillis());
            // 直接结束
            return;
        }
        String ip = IpUtil.getIp(request);
        String address = IpUtil.getIpAddr(ip);
        UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
        String browser = userAgent.getBrowser().getName() + userAgent.getVersion();
        String os = userAgent.getOs().getName();
        OnlineUser onlineUser = new OnlineUser(deviceSn, username, nickname, ip, address, browser, os, new Date());

        // 插入Redis
        redisService.set(OnlineUserConstant.USER_INFO_SET_KEY + deviceSn, onlineUser);
        redisService.addSortSet(OnlineUserConstant.DEVICE_HASH_KEY, deviceSn, System.currentTimeMillis());
        redisService.lPush(OnlineUserConstant.IP_LIST_KEY + ip, deviceSn);
        redisService.lPush(OnlineUserConstant.USERNAME_LIST_KEY + username, deviceSn);
    }

    @Override
    public BasePage selectPageList(Integer pageNum, Integer pageSize, OnlineUserParam param) {
        List<OnlineUser> onlineUserList = new ArrayList<>();
        long total = 0L;
        // 无参查询
        if (Objects.isNull(param.getIp()) && Objects.isNull(param.getUsername())) {
            Set<Object> devices = redisService.zSetPage(OnlineUserConstant.DEVICE_HASH_KEY, pageNum, pageSize, false);
            total = redisService.zSetTotal(OnlineUserConstant.DEVICE_HASH_KEY);
            devices.forEach(device -> onlineUserList.add(redisService.getObject(OnlineUserConstant.USER_INFO_SET_KEY + device, OnlineUser.class)));
        }
        // 查IP & 不查Username
        else if (Objects.nonNull(param.getIp()) && Objects.isNull(param.getUsername())) {
            if (redisService.hasKey(OnlineUserConstant.IP_LIST_KEY + param.getIp())) {
                total = redisService.lSize(OnlineUserConstant.IP_LIST_KEY + param.getIp());
                // 查IP下的所有设备
                List<Object> ipDeviceList = redisService.lRange(OnlineUserConstant.IP_LIST_KEY + param.getIp(), 0, -1);
                // 倒序排列
                Collections.reverse(ipDeviceList);
                // 手动分页
                List<Object> page = CollUtil.page(pageNum - 1, pageSize, ipDeviceList);
                page.forEach(device -> onlineUserList.add(redisService.getObject(OnlineUserConstant.USER_INFO_SET_KEY + device, OnlineUser.class)));
            }
        }
        // 查Username & 不查IP
        else if (Objects.isNull(param.getIp()) && Objects.nonNull(param.getUsername())) {
            if (redisService.hasKey(OnlineUserConstant.USERNAME_LIST_KEY + param.getUsername())) {
                total = redisService.lSize(OnlineUserConstant.USERNAME_LIST_KEY + param.getUsername());
                // 查Username下的所有设备
                List<Object> usernameDeviceList = redisService.lRange(OnlineUserConstant.USERNAME_LIST_KEY + param.getUsername(), 0, -1);
                // 倒序排列
                Collections.reverse(usernameDeviceList);
                // 手动分页
                List<Object> page = CollUtil.page(pageNum - 1, pageSize, usernameDeviceList);
                page.forEach(device -> onlineUserList.add(redisService.getObject(OnlineUserConstant.USER_INFO_SET_KEY + device, OnlineUser.class)));
            }
        }
        // 两个都查
        else if (Objects.nonNull(param.getIp()) && Objects.nonNull(param.getUsername())) {
            // 查IP下的所有设备
            List<Object> ipDeviceList = redisService.lRange(OnlineUserConstant.IP_LIST_KEY + param.getIp(), 0, -1);
            // 查Username下的所有设备
            List<Object> usernameDeviceList = redisService.lRange(OnlineUserConstant.USERNAME_LIST_KEY + param.getUsername(), 0, -1);
            // 找出交集
            Set<Object> set = CollUtil.intersectionDistinct(ipDeviceList, usernameDeviceList);
            List<Object> list = CollUtil.toList(set);
            // 设置Total
            total = set.size();
            List<Object> page = CollUtil.page(pageNum - 1, pageSize, list);
            page.forEach(device -> onlineUserList.add(redisService.getObject(OnlineUserConstant.USER_INFO_SET_KEY + device, OnlineUser.class)));

        }
        return BasePage.build(total, onlineUserList);
    }

    private Boolean isOwn(String deviceSn) {
        return deviceSn.equals(UserInfoContext.getDeviceSn());
    }

    @Override
    public void deleteByUsername(String username) {
        // 查Username下的所有设备
        List<Object> usernameDeviceList = redisService.lRange(OnlineUserConstant.USERNAME_LIST_KEY + username, 0, -1);
        if (CollUtil.isNotEmpty(usernameDeviceList)) {
            for (Object deviceSn : usernameDeviceList) {
                // 删除Redis中的信息
                deleteOne(deviceSn.toString(), false);
            }
        }
    }
}
